home *** CD-ROM | disk | FTP | other *** search
Wrap
Text File | 1994-11-17 | 52.5 KB | 1,417 lines | [ TEXT/MPS ]
{––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– PROJECT: Threads Traffic Simulation FILE: USimulation.p LANGUAGE: MPW Pascal (version 3.2) DESCRIPTION: This is where all the magic happens. Initially after the document is initialized four threads are started up, two to control the street light, one to keep a time global up to date for pre-emptive threads, and one to continually look for two free threads, one that is pre-emptive and one that is cooperative, with which it can create a new car. AUTHOR(S): William H. Knott Apple Computer Cupertino, CA 95014 AppleLink : KNOTT VERSION(S): 1.0 20-Jul-92 WHK First rev Finished today. –––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––} UNIT USimulation; INTERFACE USES MemTypes, QuickDraw, OSIntf, ToolIntf, PackIntf, Traps, Threads, UApplication, UInitMgmt, UDocument; PROCEDURE Segment_UTestSuite; PROCEDURE InitializeRoadParams; PROCEDURE CreateTrafficLight; PROCEDURE DrawTheTraficLight(fromThread : BOOLEAN); PROCEDURE CreateFreeThreadsForCars; PROCEDURE UpdateAllCars; PROCEDURE MarkACarForDestruction; CONST kMedianSize = 80; kMaxNumOfThreads = 15; kCarJumpRatio = 5; kCarLength = 15; kCarWidth = 4; kRoadWidth = 60; HellFreezesOver = FALSE; VAR gTotalCarsInArray : INTEGER; gVRoadLeft : INTEGER; gVRoadRight : INTEGER; gVRoadTop : INTEGER; gVRoadBottom : INTEGER; gHRoadLeft : INTEGER; gHRoadRight : INTEGER; gHRoadTop : INTEGER; gHRoadBottom : INTEGER; gA5Check : INTEGER; IMPLEMENTATION PROCEDURE InitializeRoadParams; VAR roadTemp : INTEGER; BEGIN gTotalCarsInArray := 0; roadTemp := (gWindowSize.right - gWindowSize.left) DIV 2 - (kRoadWidth DIV 2); gVRoadLeft := roadTemp; gVRoadRight := roadTemp + kRoadWidth; gVRoadTop := gWindowSize.top; gVRoadBottom := gWindowSize.bottom; roadTemp := (gWindowSize.bottom - gWindowSize.top) DIV 2 - (kRoadWidth DIV 2); gHRoadLeft := gWindowSize.left; gHRoadRight := gWindowSize.right; gHRoadTop := roadTemp; gHRoadBottom := roadTemp + kRoadWidth; END; PROCEDURE CreateCarGeneratingThread; FORWARD; {$S Simulation} {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} { } { Segment_UDocument } { } { Provided as a convenient way of unloading the UDocument segment when needed } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} PROCEDURE Segment_UTestSuite; BEGIN END; {$S Simulation} {–––––––––––––––––––––––––––––COOPERATIVE & PREEMPTIVE–––––––––––––––––––––––––––––-––} { } { ReturnCarHandle } { } { Return a AutoHandle to the car with the given carID. If no such carID exists, } { return NIL } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} FUNCTION ReturnCarHandle(theCarID : INTEGER) : AutoHandle; BEGIN ReturnCarHandle := NIL; IF gAutomobiles = NIL THEN EXIT(ReturnCarHandle); IF gTotalCarsInArray < theCarID THEN BEGIN DebugStr('Index is out of range'); EXIT(ReturnCarHandle) END; ReturnCarHandle := gAutomobiles^^[theCarID]; END; {$S Simulation} {––––––––––––––––––––––––––––––––––––COOPERATIVE–––––––––––––––––––––––––––––––––––-––} { } { DoesCarExist } { } { Does a car with that ID number exist in our current world of cars. Try to get } { its handle, and if successful, then return TRUE, else FALSE. } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} FUNCTION DoesCarExist(theCarID : INTEGER) : BOOLEAN; VAR theCar : AutoHandle; BEGIN theCar := ReturnCarHandle(theCarID); DoesCarExist := NOT (theCar = NIL); END; {$S Simulation} {––––––––––––––––––––––––––––––––––––COOPERATIVE–––––––––––––––––––––––––––––––––––-––} { } { GetUniqueCarID } { } { Keep generating a new number and seeing if it exists. Once it does not, return } { that number } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} FUNCTION GetUniqueCarID : INTEGER; VAR startCount : INTEGER; newCar : INTEGER; BEGIN newCar := 0; IF gTotalCarsInArray= 0 THEN BEGIN GetUniqueCarID := 1; EXIT(GetUniqueCarID); END; FOR startCount := 1 TO gTotalCarsInArray DO BEGIN IF NOT DoesCarExist(startCount) THEN newCar := startCount; END; IF newCar = 0 THEN GetUniqueCarID := gTotalCarsInArray + 1 ELSE GetUniqueCarID := newCar; END; {$S Simulation} {––––––––––––––––––––––––––––––––––––COOPERATIVE–––––––––––––––––––––––––––––––––––-––} { } { GetStartingCarPointAndLocation } { } { Getting a startign point for a new car. There are only four valid starting } { points for now. } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} PROCEDURE GetStartingCarPointAndLocation(VAR position : POINT; VAR direction : INTEGER); BEGIN direction := (abs(Random) MOD 4) * 90; CASE direction OF 0: SetPt(position, gHRoadLeft + 3, gHRoadBottom - 8); 90: SetPt(position, gVRoadRight - 8, gVRoadBottom); 180: SetPt(position, gHRoadRight, gHRoadTop + 9); 270: SetPt(position, gVRoadLeft + 8, gVRoadTop); OTHERWISE BEGIN direction := 0; SetPt(position, gHRoadLeft + 3, gHRoadBottom - 8); END; END; END; {$S Simulation} {––––––––––––––––––––––––––––––––––––COOPERATIVE–––––––––––––––––––––––––––––––––––-––} { } { AddAutoMobileToList } { } { Add a new car handle to the list of all the cars on the road. Dynamic array is } { used to store all of the cars. } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} PROCEDURE AddAutoMobileToList(newRefNum : INTEGER; aCar : AutoHandle); VAR error : OSErr; BEGIN IF gAutomobiles = NIL THEN gAutomobiles := AutoListArrayHdl(NewHandleClear(SizeOf(Handle))); error := ThreadBeginCritical; { This is a global used by other threads and could move, so prevent others from using it } IF (GetHandleSize(Handle(gAutomobiles)) DIV SizeOF(Handle)) < newRefNum THEN SetHandleSize(Handle(gAutomobiles), newRefNum * SizeOf(Handle)); IF MemError <> noErr THEN DebugSTr('Inadequate memory'); gAutomobiles^^[newRefNum] := aCar; gTotalCarsInArray := GetHandleSize(Handle(gAutomobiles)) DIV 4; error := ThreadEndCritical; { OK, done my stuff with the handle, others can use it now. } END; {$S Simulation} {––––––––––––––––––––––––––––––––––––COOPERATIVE–––––––––––––––––––––––––––––––––––-––} { } { CreateANewAutomobile } { } { Creates a new automobile in the car list. Parameters like direction and car } { color are random, everything else is fixed for all cars. } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} PROCEDURE CreateANewAutomobile(newRefNum : INTEGER); VAR aCar : AutoHandle; BEGIN aCar := AutoHandle(NewHandleClear(SizeOf(AutoRec))); MoveHHi(Handle(aCar)); HLock(Handle(aCar)); { Car cannot move in memory of Async threads could get screwed! } WITH aCar^^ DO BEGIN GetStartingCarPointAndLocation(position, direction); carType := (abs(RANDOM) MOD 5); { Right now only means the color of the car. } speed := 40; { Only 40 or zero currently, not much in way of variance for first run. } accelleration := 1; { Only 1, way too complex for first time around. } braking := 1; { Only 1, way too complex for first time around. } needToRedraw := TRUE; { First Time, definate draw needed } carIsDone := FALSE; { Car is still on the map! } recalcCarShape := FALSE; { Only needed when a car has turned } markedForDeath := FALSE; { We do not want to destroy the car. } oldTrapAddr := NIL; { Initialize to NIL so switcher outer does not do bad things. } IF (Random MOD 10) IN [0,1] THEN turning := TRUE ELSE turning := FALSE; END; AddAutoMobileToList(newRefNum, aCar); END; {$S Simulation} {––––––––––––––––––––––––––––––––––––COOPERATIVE–––––––––––––––––––––––––––––––––––-––} { } { CarDrawingThread } { } { As the car moves along the road, we need to draw its new position and erase } { its old one. This code does just that, and continues to draw the car until a } { variable is set by the pre-emptive thread telling it to dispose of itself. } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} FUNCTION CarDrawingThread(theCarRefID : LONGINT) : LONGINT; VAR theCar : AutoHandle; error : OSErr; oldCarLoc : POINT; notFirstTime : BOOLEAN; carRect : RECT; direction : INTEGER; carIsErased : BOOLEAN; myThreadID : ThreadID; markedForDeath : BOOLEAN; BEGIN theCar := ReturnCarHandle(theCarRefID); SetPt(oldCarLoc, -1, -1); direction := theCar^^.direction; carIsErased := FALSE; IF theCar = NIL THEN DebugStr('Bogus, no such car') ELSE BEGIN notFirstTime := FALSE; repeat IF theCar^^.needToRedraw THEN { We need to redraw the car! } BEGIN error := ThreadBeginCritical; { I do not want other threads to change car data while I am in the middle of drawing the car } SetPort(gDocument^^.docWindow); IF notFirstTime THEN BEGIN ForeColor(BlackColor); IF direction IN [180,0] THEN SetRect(carRect, oldCarLoc.h, oldCarLoc.v - kCarWidth, oldCarLoc.h + kCarLength, oldCarLoc.v + kCarWidth) ELSE SetRect(carRect, oldCarLoc.h - kCarWidth, oldCarLoc.v, oldCarLoc.h + kCarWidth, oldCarLoc.v + kCarLength); FillRect(carRect, gray); END ELSE notFirstTime := TRUE; IF theCar^^.recalcCarShape THEN BEGIN direction := theCar^^.direction; theCar^^.recalcCarShape := FALSE; END; IF direction IN [180,0] THEN SetRect(carRect, theCar^^.position.h, theCar^^.position.v - kCarWidth, theCar^^.position.h + kCarLength, theCar^^.position.v + kCarWidth) ELSE SetRect(carRect, theCar^^.position.h - kCarWidth, theCar^^.position.v, theCar^^.position.h + kCarWidth, theCar^^.position.v + kCarLength); CASE theCar^^.carType OF 1: ForeColor(kColorOne); 2: ForeColor(kColorTwo); 3: ForeColor(kColorThree); 4: ForeColor(kColorFour); OTHERWISE ForeColor(YellowColor); END; IF theCar^^.carType = 5 THEN ForeColor(WhiteColor); FillRect(carRect, black); ForeColor(BlackColor); oldCarLoc := theCar^^.position; theCar^^.needToRedraw := FALSE; error := ThreadEndCritical; { Done drawing the car, let others change away. } END; error := YieldToAnyThread; { Give the other cooperative threads time to do their drawing and handle events. } until theCar^^.carIsDone; SetPort(gDocument^^.docWindow); ForeColor(BlackColor); IF direction IN [180,0] THEN SetRect(carRect, oldCarLoc.h, oldCarLoc.v - kCarWidth, oldCarLoc.h + kCarLength, oldCarLoc.v + kCarWidth) ELSE SetRect(carRect, oldCarLoc.h - kCarWidth, oldCarLoc.v, oldCarLoc.h + kCarWidth, oldCarLoc.v + kCarLength); FillRect(carRect, gray); END; markedForDeath := theCar^^.markedForDeath; DisposHandle(Handle(gAutomobiles^^[theCarRefID])); gAutomobiles^^[theCarRefID] := NIL; error := GetCurrentThread(myThreadID); { I wish to recycle this thread(possibly), so I need to know my own ID. } IF markedForDeath THEN error := DisposeThread(myThreadID, 0, FALSE) { Dispose of myself, do not recycle the threaed into the thread pool. } ELSE error := DisposeThread(myThreadID, 0, TRUE); { Dispose of myself, recycle the threaed into the thread pool. } END; {$S Simulation} {––––––––––––––––––––––––––––––––––––COOPERATIVE–––––––––––––––––––––––––––––––––––-––} { } { UpdateAllCars } { } { Something could cause an update of our simulated world,and if a car is not } { moving, it would dissapear until it began to move again. Lets fix it so that } { all the cars will be updated on an update event. } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} PROCEDURE UpdateAllCars; VAR hndlSize : LONGINT; numOfCars : INTEGER; loop : INTEGER; theCar : AutoHandle; BEGIN IF gAutomobiles = NIL THEN EXIT(UpdateAllCars); hndlSize := GetHandleSize(Handle(gAutomobiles)); numOfCars := LoWord(hndlSize) DIV 4; FOR loop := 1 TO numOfCars DO BEGIN theCar := ReturnCarHandle(loop); IF theCar <> NIL THEN BEGIN theCar^^.needToRedraw := TRUE; END; END; END; {$S Simulation} {–––––––––––––––––––––––––––––––––––––PREEMPTIVE–––––––––––––––––––––––––––––––––––-––} { } { CarLeavesPlayingField } { } { Cars would continue to go forever after they left the playing field if we did } { not deter mine when if left the window so that we could re-use its thread. } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} FUNCTION CarLeavesPlayingField(carLoc : POINT) : BOOLEAN; BEGIN CarLeavesPlayingField := TRUE; IF gWindowSize.left > (carLoc.h + kCarLength) THEN EXIT(CarLeavesPlayingField); IF gWindowSize.right < (carLoc.h) THEN EXIT(CarLeavesPlayingField); IF gWindowSize.top > (carLoc.v + kCarLength) THEN EXIT(CarLeavesPlayingField); IF gWindowSize.bottom < (carLoc.v) THEN EXIT(CarLeavesPlayingField); CarLeavesPlayingField := FALSE; END; {$S Simulation} {–––––––––––––––––––––––––––––––––––––PREEMPTIVE–––––––––––––––––––––––––––––––––––-––} { } { AmIAtIntersection } { } { Well, am I even at the intersection. The car calculation needs to handle things } { differently when we are in the intersection. This will let me know whether we } { have entered the intersection or not. } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} FUNCTION AmIAtIntersection(carDir : INTEGER; carLoc : POINT) : BOOLEAN; VAR disToLight : INTEGER; BEGIN CASE carDir OF 0: disToLight := abs(gVRoadLeft - (carLoc.h + kCarLength)); { Car Size needs to be calculated! } 90: disToLight := abs(gHRoadBottom - carLoc.v); 180: disToLight := abs(gVRoadRight - carLoc.h); 270: disToLight := abs(gHRoadTop - (carLoc.v + kCarLength)); END; AmIAtIntersection := (disToLight < (kCarJumpRatio + 1)); END; {$S Simulation} {–––––––––––––––––––––––––––––––––––––PREEMPTIVE–––––––––––––––––––––––––––––––––––-––} { } { IsLightRed } { } { Quick and dirty little routine to tell me whether the traffic light is red for } { the direction that I am going. } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} FUNCTION IsLightRed(carDir : INTEGER) : BOOLEAN; BEGIN IsLightRed := FALSE; IF (carDir = 0) | (carDir = 180) THEN BEGIN IF gTrafficState.lightState IN [0,4,5,6,7] THEN IsLightRed := TRUE; END ELSE BEGIN IF gTrafficState.lightState IN [0,1,2,3,4] THEN IsLightRed := TRUE; END; END; {$S Simulation} {–––––––––––––––––––––––––––––––––––––PREEMPTIVE–––––––––––––––––––––––––––––––––––-––} { } { IsLightGreen } { } { Quick and dirty little routine to tell me whether the traffic light is green } { for the the direction that I am going. } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} FUNCTION IsLightGreen(carDir : INTEGER) : BOOLEAN; BEGIN IsLightGreen := FALSE; IF (carDir = 0) | (carDir = 180) THEN BEGIN IF gTrafficState.lightState = 2 THEN IsLightGreen := TRUE; END ELSE BEGIN IF gTrafficState.lightState = 6 THEN IsLightGreen := TRUE; END; END; {$S Simulation} {–––––––––––––––––––––––––––––––––––––PREEMPTIVE–––––––––––––––––––––––––––––––––––-––} { } { IsLightArrow } { } { Quick and dirty little routine to tell me whether the traffic light is a green } { arrow for the direction that I am going. } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} FUNCTION IsLightArrow(carDir : INTEGER) : BOOLEAN; BEGIN IsLightArrow := FALSE; IF (carDir = 0) | (carDir = 180) THEN BEGIN IF gTrafficState.lightState = 1 THEN IsLightArrow := TRUE; END ELSE BEGIN IF gTrafficState.lightState = 5 THEN IsLightArrow := TRUE; END; END; {$S Simulation} {––––––––––––––––––––––––––––––––––––COOPERATIVE–––––––––––––––––––––––––––––––––––-––} { } { AmIOnTopOfACar } { } { When placing new cars onto the map, I need to see whether there is already a car } { in the position that I just placed a new car. If there is, I had probably better } { do something about it. } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} FUNCTION AmIOnTopOfACar(carLoc : POINT; carDir : INTEGER; carID : INTEGER) : BOOLEAN; VAR loop : INTEGER; aCar : AutoHandle; distancebetweencars : INTEGER; BEGIN AmIOnTopOfACar := FALSE; FOR loop := 1 TO gTotalCarsInArray DO BEGIN IF carID = loop THEN Cycle; aCar := ReturnCarHandle(loop); IF (aCar <> NIL) & (aCar^^.direction = carDir) THEN BEGIN IF (aCar^^.position.v = carLoc.v) AND (aCar^^.position.h = carLoc.h) THEN AmIOnTopOfACar := TRUE; distancebetweencars := abs(aCar^^.position.h - carLoc.h) + abs(aCar^^.position.v - carLoc.v) ; IF (distancebetweencars < (kCarLength * 2)) THEN AmIOnTopOfACar := TRUE; END; END; END; {$S Simulation} {–––––––––––––––––––––––––––––––––––––PREEMPTIVE–––––––––––––––––––––––––––––––––––-––} { } { IsThereACarStoppedInFrontOfMe } { } { When this simulation was first coded, cars did not seem to realise that two of } { them could not occupy the same space at the same time. This attempts to solve } { some of these problems by looking to see if there is a car in front of the } { current car and whether it is stopped or not. If it is stopped, then we had } { better stop. } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} FUNCTION IsThereACarStoppedInFrontOfMe(carLoc : POINT; carDir : INTEGER; carID : INTEGER) : BOOLEAN; VAR loop : INTEGER; aCar : AutoHandle; distancebetweencars : INTEGER; onLine : BOOLEAN; error : OSErr; BEGIN IsThereACarStoppedInFrontOfMe := FALSE; FOR loop := 1 TO gTotalCarsInArray DO BEGIN IF carID = loop THEN Cycle; error := ThreadBeginCritical; { Do not want other threads to move cars while I am trying to determine if there is a car in front of me. } aCar := ReturnCarHandle(loop); IF (aCar <> NIL) & (aCar^^.direction = carDir) THEN BEGIN IF (aCar^^.position.v = carLoc.v) AND (aCar^^.position.h = carLoc.h) THEN BEGIN END; IF aCar^^.speed = 0 THEN { If this car is going our direction, and is stopped, we may need to stop! } BEGIN CASE aCar^^.direction OF 0: distancebetweencars := aCar^^.position.h - carLoc.h; 90: distancebetweencars := -(aCar^^.position.v - carLoc.v); 180: distancebetweencars := -(aCar^^.position.h - carLoc.h); 270: distancebetweencars := aCar^^.position.v - carLoc.v; END; onLine := TRUE; IF aCar^^.direction IN [0,180] THEN onLine := abs(aCar^^.position.v - carLoc.v) < 10; IF aCar^^.direction IN [90, 270] THEN onLine := abs(aCar^^.position.h - carLoc.h) < 10; IF ((((kCarLength * 3) DIV 2) >= distancebetweencars) AND (distancebetweencars > 0)) AND onLine THEN IsThereACarStoppedInFrontOfMe := TRUE; END; END; error := ThreadEndCritical; { Done my determination, all cars are free to move again. } END; END; {$S Simulation} {–––––––––––––––––––––––––––––––––––––PREEMPTIVE–––––––––––––––––––––––––––––––––––-––} { } { AmIAtLangeChangePoint } { } { If I am a car that is planning to turn left, I need to see whether I am at the } { point where I need to start moving into the left hand turn lane. } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} FUNCTION AmIAtLangeChangePoint(carID : INTEGER) : BOOLEAN; VAR aCar : AutoHandle; turningPoint : BOOLEAN; BEGIN AmIAtLangeChangePoint := FALSE; aCar := ReturnCarHandle(carID); CASE aCar^^.direction OF 0: turningPoint := (aCar^^.position.h >= gVRoadLeft - kMedianSize); 90: turningPoint := (aCar^^.position.v <= gHRoadBottom + kMedianSize); 180: turningPoint := (aCar^^.position.h <= gVRoadRight + kMedianSize); 270: turningPoint := (aCar^^.position.v >= gHRoadTop - kMedianSize); END; AmIAtLangeChangePoint := turningPoint; END; {$S Simulation} {–––––––––––––––––––––––––––––––––––––PREEMPTIVE–––––––––––––––––––––––––––––––––––-––} { } { AmIAtTurnPoint } { } { This should be made more flexible, but for now the simulation only has a single } { intersection. Lets me know whether my car is at a point at which I need to turn } { left. Right turns have not yet been implemented. } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} FUNCTION AmIAtTurnPoint(carID : INTEGER) : BOOLEAN; VAR aCar : AutoHandle; isThere : BOOLEAN; BEGIN AmIAtTurnPoint := FALSE; aCar := ReturnCarHandle(carID); CASE aCar^^.direction OF 0: isThere := (aCar^^.position.h >= gVRoadRight - 10); 90: isThere := (aCar^^.position.v <= gHRoadTop + 10); 180: isThere := (aCar^^.position.h <= gVRoadLeft + 10); 270: isThere := (aCar^^.position.v >= gHRoadBottom - 10); END; AmIAtTurnPoint := isThere; END; {$S Simulation} {–––––––––––––––––––––––––––––––––––––PREEMPTIVE–––––––––––––––––––––––––––––––––––-––} { } { CalcCarParams } { } { This is where all the magic happens. This is the preemptive thread that } { calculates the current location of the car in reagrds to other cars and the } { speed and direction that I should be going. Since this is a preemptive thread, } { it would not be prudent to call any toolbox calls from here. } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} FUNCTION CalcCarParams(theCarRefID : LONGINT) : LONGINT; VAR carLoc : POINT; cardir : INTEGER; madeDecisionToTurn : BOOLEAN; turningRight : BOOLEAN; turningLeft : BOOLEAN; changingLane : BOOLEAN; lastMoveTime : LONGINT; theTime : LONGINT; error : OSErr; hellFreezesOver : BOOLEAN; theCarData : AutoHandle; boundsArea : RECT; direction : INTEGER; myThreadID : ThreadID; turning : BOOLEAN; shiftDist : INTEGER; markedForDeath : BOOLEAN; BEGIN madeDecisionToTurn := FALSE; changingLane := FALSE; boundsArea := WindowPtr(FrontWindow)^.portRect; InsetRect(boundsArea, -5, -5); lastMoveTime := gCurrentTime; { Could have a problem, but not likely to be a problem! } theCarData := ReturnCarHandle(theCarRefID); direction := theCarData^^.direction; carLoc := theCarData^^.position; turning := theCarData^^.turning; shiftDist := 0; repeat theTime := gCurrentTime; IF theTime > (lastMoveTime + 4) THEN BEGIN error := ThreadBeginCritical; { Do not want to redraw the car until I have determined its new location and direction } IF theCarData^^.speed > 0 THEN BEGIN CASE direction OF 0: theCarData^^.position.h := theCarData^^.position.h + 4; 90: theCarData^^.position.v := theCarData^^.position.v -4; 180: theCarData^^.position.h := theCarData^^.position.h - 4; 270: theCarData^^.position.v := theCarData^^.position.v + 4; END; carLoc := theCarData^^.position; lastMoveTime := theTime; IF turning THEN BEGIN IF AmIAtTurnPoint(theCarRefID) THEN BEGIN theCarData^^.direction := theCarData^^.direction + 90; IF theCarData^^.direction > 270 THEN theCarData^^.direction := 0; direction := theCarData^^.direction; theCarData^^.recalcCarShape := TRUE; turning := FALSE; END ELSE IF AmIAtLangeChangePoint(theCarRefID) THEN BEGIN shiftDist := shiftDist + 1; IF shiftDist < 11 THEN BEGIN CASE direction OF 0: theCarData^^.position.v := theCarData^^.position.v - 2; 90: theCarData^^.position.h := theCarData^^.position.h -2; 180: theCarData^^.position.v := theCarData^^.position.v + 2; 270: theCarData^^.position.h := theCarData^^.position.h + 2; END; END; END; END; theCarData^^.needToRedraw := TRUE; IF (IsLightRed(direction) | IsLightArrow(direction)) & AmIAtIntersection(direction, carLoc) THEN theCarData^^.speed := 0 ELSE IF IsThereACarStoppedInFrontOfMe(carLoc, direction, theCarRefID) THEN theCarData^^.speed := 0 ELSE IF NOT (IsLightArrow(direction)) & AmIAtIntersection(direction, carLoc) THEN theCarData^^.speed := 0 END ELSE BEGIN IF (NOT AmIAtIntersection(direction, carLoc)) & (NOT IsThereACarStoppedInFrontOfMe(carLoc, direction, theCarRefID)) THEN BEGIN theCarData^^.speed := 40; lastMoveTime := theTime; END ELSE IF (IsLightGreen(direction) & NOT(IsThereACarStoppedInFrontOfMe(carLoc, direction, theCarRefID))) & ((NOT turning) | (NOT AmIAtLangeChangePoint(theCarRefID))) THEN BEGIN theCarData^^.speed := 40; lastMoveTime := theTime; END ELSE IF turning & IsLightArrow(direction) & NOT IsThereACarStoppedInFrontOfMe(carLoc, direction, theCarRefID) THEN BEGIN theCarData^^.speed := 40; lastMoveTime := theTime; END END; error := ThreadEndCritical; { Done moving/turning the car, OK for drawing threads to redraw the car now if necessary. } END; error := YieldToAnyThread; { Not required for pre-emptive threads, but we wish to give up time now to be polite } until CarLeavesPlayingField(carLoc); error := GetCurrentThread(myThreadID); { I wish to recycle this thread(possibly), so I need to know my own ID. } markedForDeath := theCarData^^.markedForDeath; theCarData^^.carIsDone := TRUE; IF markedForDeath THEN error := DisposeThread(myThreadID, 0, FALSE) { Dispose of myself, do not recycle the threaed into the thread pool. } ELSE error := DisposeThread(myThreadID, 0, TRUE); { Dispose of myself, recycle the threaed into the thread pool. } END; {$S Simulation} {–––––––––––––––––––––––––––––––––––––COOPERATIVE––––––––––––––––––––––––––––––––––-––} { } { DrawTheTraficLight } { } { Now we are at the place that actually draws the traffic light. Just a lot of } { QuickDraw drawing, so we better not be called from a pre-emptive thread. } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} PROCEDURE DrawTheTraficLight(fromThread : BOOLEAN); VAR tempRect : RECT; sidePoly : PolyHandle; lightRect : Rect; holdPort : GrafPtr; PROCEDURE BlackOutCircle(theCircRect : RECT); BEGIN ForeColor(BlackColor); FillOval(theCircRect, white) END; BEGIN GetPort(holdPort); IF fromThread THEN SetPort(gDocument^^.docWindow); IF NOT(fromThread) THEN BEGIN SetRect(tempRect, gVRoadRight + 20, gHRoadTop - 125, gVRoadRight + 50, gHRoadTop - 7); ForeColor(BlackColor); FillRect(tempRect, white); sidePoly := OpenPoly; Moveto(tempRect.left, tempRect.top); Lineto(tempRect.right, tempRect.top); Lineto(tempRect.right + 35, tempRect.top - 15); Lineto(tempRect.right + 35, tempRect.bottom - 15); Lineto(tempRect.right, tempRect.bottom); Lineto(tempRect.left, tempRect.bottom); Lineto(tempRect.left, tempRect.top); ClosePoly; FillPoly(sidePoly, white); FramePoly(sidePoly); Moveto(tempRect.right + 1, tempRect.top); Lineto(tempRect.right + 1, tempRect.bottom); KillPoly(sidePoly); END; IF (fromThread AND gTrafficState.dispStateChanged) OR NOT(fromThread) THEN BEGIN SetRect(tempRect, gVRoadRight + 20, gHRoadTop - 125, gVRoadRight + 50, gHRoadTop - 7); SetRect(lightRect, tempRect.left + 5, tempRect.top + 5, tempRect.right - 5, tempRect.top + 25); BlackOutCircle(lightRect); ForeColor(RedColor); IF gTrafficState.lightState IN [0,1,2,3,4] THEN PaintOval(lightRect) ELSE FrameOval(lightRect); SetRect(lightRect, tempRect.left + 5, tempRect.top + 35, tempRect.right - 5, tempRect.top + 55); BlackOutCircle(lightRect); ForeColor(YellowColor); IF gTrafficState.lightState = 7 THEN PaintOval(lightRect) ELSE FrameOval(lightRect); SetRect(lightRect, tempRect.left + 5, tempRect.top + 65, tempRect.right - 5, tempRect.top + 85); BlackOutCircle(lightRect); ForeColor(GreenColor); IF gTrafficState.lightState = 6 THEN PaintOval(lightRect) ELSE FrameOval(lightRect); SetRect(lightRect, tempRect.left + 5, tempRect.top + 95, tempRect.right - 5, tempRect.top + 115); BlackOutCircle(lightRect); ForeColor(GreenColor); { This is for the green Arrow } FrameOval(lightRect); IF gTrafficState.lightState = 5 THEN BEGIN Moveto(lightRect.left + 3, lightRect.top + (lightRect.bottom - lightRect.top) DIV 2); Lineto(lightRect.right - 3, lightRect.top + (lightRect.bottom - lightRect.top) DIV 2); Moveto(lightRect.left +7, lightRect.top + (lightRect.bottom - lightRect.top) DIV 2 - 4); Lineto(lightRect.left +3, lightRect.top + (lightRect.bottom - lightRect.top) DIV 2); Lineto(lightRect.left +7, lightRect.top + (lightRect.bottom - lightRect.top) DIV 2 + 4); END; SetRect(lightRect, tempRect.right + 7, tempRect.top - 2, tempRect.right + 27, tempRect.top + 18); BlackOutCircle(lightRect); ForeColor(RedColor); IF gTrafficState.lightState IN [0,4, 5, 6, 7] THEN PaintOval(lightRect) ELSE FrameOval(lightRect); SetRect(lightRect, tempRect.right + 7, tempRect.top + 28, tempRect.right + 27, tempRect.top + 48); BlackOutCircle(lightRect); ForeColor(YellowColor); IF gTrafficState.lightState = 3 THEN PaintOval(lightRect) ELSE FrameOval(lightRect); SetRect(lightRect, tempRect.right + 7, tempRect.top + 58, tempRect.right + 27, tempRect.top + 78); BlackOutCircle(lightRect); ForeColor(GreenColor); IF gTrafficState.lightState = 2 THEN PaintOval(lightRect) ELSE FrameOval(lightRect); SetRect(lightRect, tempRect.right + 7, tempRect.top + 88, tempRect.right + 27, tempRect.top + 108); BlackOutCircle(lightRect); ForeColor(GreenColor); { This is for the green Arrow } FrameOval(lightRect); IF gTrafficState.lightState = 1 THEN BEGIN Moveto(lightRect.left + 3, lightRect.top + (lightRect.bottom - lightRect.top) DIV 2); Lineto(lightRect.right - 3, lightRect.top + (lightRect.bottom - lightRect.top) DIV 2); Moveto(lightRect.left +7, lightRect.top + (lightRect.bottom - lightRect.top) DIV 2 - 4); Lineto(lightRect.left +3, lightRect.top + (lightRect.bottom - lightRect.top) DIV 2); Lineto(lightRect.left +7, lightRect.top + (lightRect.bottom - lightRect.top) DIV 2 + 4); END; gTrafficState.dispStateChanged := FALSE; END; SetPort(holdPort); END; {$S Simulation} {–––––––––––––––––––––––––––––––––––––COOPERATIVE––––––––––––––––––––––––––––––––––-––} { } { TrafficLightToolboxThread } { } { This threads purpose is to continually call DrawTheTrafficLight until the end of } { time, well, at least until the application ends or it gets killed. } { DrawTheTraficLight will do the determination as to whether it really needs to } { redraw the light or not. } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} FUNCTION TrafficLightToolboxThread(threadParam : LONGINT) : LONGINT; VAR error : OSErr; BEGIN repeat DrawTheTraficLight(TRUE); error := YieldToAnyThread; { Yield to other threads. } until FALSE; END; {$S Simulation} {–––––––––––––––––––––––––––––––––––––COOPERATIVE––––––––––––––––––––––––––––––––––-––} { } { TimeKeeper } { } { A cooperative thread, whose only purpose is to stuff a global variable with the } { value gotten from TickCount. A lot of pre-emptive threads rely on this data. } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} FUNCTION TimeKeeper(threadParam : LONGINT) : LONGINT; VAR error : OSErr; BEGIN repeat IF TickCount <> gCurrentTime THEN BEGIN error := ThreadBeginCritical; { Make sure other threads cannot access gCurrentTime until I am done changing it. } gCurrentTime := TickCount; error := ThreadEndCritical; { Done changing it, can be used by other threads now. } END; error := YieldToAnyThread; { Yield to other threads. } until FALSE; END; {$S Simulation} {–––––––––––––––––––––––––––––––––––––PREEMPTIVE–––––––––––––––––––––––––––––––––––-––} { } { TrafficLightCounter } { } { This is a pre-emptive thread which rotates the light through its various phases. } { In locations in which global variable structure are shared with other threads and } { could be in a state of flux, I have bracketed them with ThreadBeginCritical/ } { ThreadEndCritical. It will go on forever, someone else will need to kill it. } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} FUNCTION TrafficLightCounter(threadParam : LONGINT) : LONGINT; VAR hellFreezesOver : BOOLEAN; error : OSErr; lastTimeSwitch : LONGINT; currentState : INTEGER; lightPhaseTimes : ARRAY[0..3] OF INTEGER; loop : INTEGER; theTime : LONGINT; BEGIN FOR loop := 0 TO 3 DO lightPhaseTimes[loop] := gTrafficState.phaseTimes[loop]; currentState := gTrafficState.lightState; lastTimeSwitch := gCurrentTime; hellFreezesOver := FALSE; repeat theTime := gCurrentTime; IF theTime > (lastTimeSwitch + (lightPhaseTimes[(currentState MOD 4)] * 60)) THEN BEGIN error := ThreadBeginCritical; { Do not let traffic light redraw until its state has been completely changed. } gTrafficState.lightState := (gTrafficState.lightState + 1) MOD 8; currentState := gTrafficState.lightState; gTrafficState.dispStateChanged := TRUE; lastTimeSwitch := theTime; error := ThreadEndCritical; { Done setting new condition, let traffic light be redrawn. } END; error := YieldToAnyThread; { Yield to other threads. } until hellFreezesOver; END; {$S Simulation} {––––––––––––––––––––––––––––––––––COOPERATIVE–––––––––––––––––––––––––––––––––––––-––} { } { InitializeTrafficLightVariables } { } { Initialize the variables that will be manipulate by the traffic light thread. } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} PROCEDURE InitializeTrafficLightVariables; BEGIN gTrafficState.lightState := 0; gTrafficState.phaseTimes[0] := 1; { Both Red, only occurs for one second } gTrafficState.phaseTimes[1] := 4; { Green Arrow, occurs for 8 seconds } gTrafficState.phaseTimes[2] := 10; { Solid Green, holds for 20 seconds } gTrafficState.phaseTimes[3] := 2; { Yellow light, 3 second hold } gTrafficState.dispStateChanged := FALSE; { Do not redraw state, has not yet changed } END; {$S Simulation} {––––––––––––––––––––––––––––––––––APPLICATION–––––––––––––––––––––––––––––––––––––-––} { } { CreateTrafficLight } { } { We need to create a traffic light for the intersection before we can run cars } { through the intersection, think of the mayhem without one. At the same time I } { will create a thread for providing time to the pre-emptive threads, which are not } { allowed to call TickCount. } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} PROCEDURE CreateTrafficLight; VAR error : OSErr; newThreadID : ThreadID; BEGIN InitializeTrafficLightVariables; gCurrentTime := TickCount; error := CreateThreadPool(kPreemptiveThread, 1, 0); { Allocate Preemptive threads to the application thread pool. } IF error <> noErr THEN DebugStr('failure to create asynch thread for the street light'); error := CreateThreadPool(kCooperativeThread, 2, 0); { Allocate Cooperative threads to the application thread pool. } IF error <> noErr THEN DebugStr('failure to create toolbox thread for the street light'); error := NewThread(kCooperativeThread, @TimeKeeper, 0, 0, kUsePremadeThread, NIL, newThreadID); { Create new cooperative thread from the pool. } IF error <> noErr THEN DebugStr('failure to create the thread TimeKeeper'); error := NewThread(kCooperativeThread, @TrafficLightToolboxThread, 0, 0, kUsePremadeThread, NIL, newThreadID); { Create new cooperative thread from the pool. } IF error <> noErr THEN DebugStr('failure to create the traffic light toolbox thread'); error := NewThread(kPreemptiveThread, @TrafficLightCounter, 0, 0, kUsePremadeThread, NIL, newThreadID); { Create new preemptive thread from the pool. } IF error <> noErr THEN DebugStr('failure to create traffic light control thread'); CreateCarGeneratingThread; END; {$S Simulation} {–––––––––––––––––––––––––––––PREEMPTIVE–THREADSWITCHER––––––––––––––––––––––––––––-––} { } { InDeep } { } { Quite simple, called only if LoadSeg is called while the current thread is the } { pre-emptive thread of one of the cars. } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} PROCEDURE InDeep; BEGIN DebugStr('Hey, no LoadSeg Calls allowed here!!!!!'); END; {$S Simulation} {–––––––––––––––––––––––––––––PREEMPTIVE–THREADSWITCHER––––––––––––––––––––––––––––-––} { } { SwitcherInner } { } { In a pre-emptive thread, it would be bad to call LoadSeg. This Switcher was set } { up to patch LoadSeg and replace it with my call in case LoadSeg was called } { anytime within the cars pre-emptive thread. If InDeep is ever executed, you are } { already in bad shape. } { } { Note: There are many other calls that should not be done from a pre-emptive } { thread. } { } { July 15, 1992 Created by William Knott } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} PROCEDURE SwitcherInner(threadID : ThreadID; data : LONGINT); VAR aCar : AutoHandle; BEGIN aCar := ReturnCarHandle(data); IF aCar <> NIL THEN BEGIN aCar^^.oldTrapAddr := Handle(GetTrapAddress(_LoadSeg)); SetTrapAddress(LONGINT(@InDeep), _LoadSeg); END; END; {$S Simulation} {–––––––––––––––––––––––––––––PREEMPTIVE–THREADSWITCHER––––––––––––––––––––––––––––-––} { } { SwitcherOuter } { } { SwitcherInner caused LoadSeg to get patched with my routine, we need to restore } { the LoadSeg trap address. } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} PROCEDURE SwitcherOuter(threadID : ThreadID; data : LONGINT); VAR aCar : AutoHandle; BEGIN aCar := ReturnCarHandle(data); IF aCar <> NIL THEN BEGIN IF aCar^^.oldTrapAddr <> NIL THEN SetTrapAddress(LONGINT(aCar^^.oldTrapAddr), _LoadSeg); END; END; {$S Simulation} {––––––––––––––––––––––––––––––––––––COOPERATIVE–––––––––––––––––––––––––––––––––––-––} { } { AddACarToTheRoad } { } { In generating new cars, we have determined that are at least two free threads, } { one of type cooperative, and one of type pre-emptive. We first create a new car } { in the car list so that we can pass a reference to it to both threads required to } { make a car go. We will also be setting routines that will be called whenever the } { cars pre-emptive threads are switched into or out of. If there is no free place } { on the board for a new car, then we will not create one for now. } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} PROCEDURE AddACarToTheRoad; VAR error : OSErr; uniqueCarID : INTEGER; newThreadID : ThreadID; aCar : AutoHandle; positionBogus : BOOLEAN; BEGIN uniqueCarID := GetUniqueCarID; CreateANewAutomobile(uniqueCarID); aCar := ReturnCarHandle(uniqueCarID); positionBogus := AmIOnTopOfACar(aCar^^.position, aCar^^.direction, uniqueCarID); IF NOT positionBogus THEN BEGIN error := NewThread(kPreemptiveThread, @CalcCarParams, uniqueCarID, 0, kUsePremadeThread + kNewSuspend, NIL, newThreadID); { Create a new thread from the application } { thread pool and set its state to suspended. } IF error <> noErr THEN DebugStr('Failure to create car position calculating thread') ELSE BEGIN error := SetThreadSwitcher (newThreadID, ThreadSwitchProcPtr(@SwitcherInner), uniqueCarID, TRUE); { Set the procedure SwitcherInner to be called whenever } { we switch into this preemptive thread. } IF error <> noErr THEN DebugStr('failure to set innie'); error := SetThreadSwitcher (newThreadID, ThreadSwitchProcPtr(@SwitcherOuter), uniqueCarID, FALSE); { Set the procedure SwitcherOuter to be called whenever } { we switch out of this preemptive thread. } IF error <> noErr THEN DebugStr('failure to set outie'); END; error := SetThreadState (newThreadID, kReadyThreadState, kNoThreadID); error := NewThread(kCooperativeThread, @CarDrawingThread, uniqueCarID, 0, kUsePremadeThread, NIL, newThreadID); { Create a preemptive thread from the application } { for drawing the car on the road. } IF error <> noErr THEN DebugStr('failure to create traffic light drawing thread'); END ELSE BEGIN DisposHandle(Handle(gAutomobiles^^[uniqueCarID])); gAutomobiles^^[uniqueCarID] := NIL; END; END; {$S Simulation} {––––––––––––––––––––––––––––––––––––COOPERATIVE–––––––––––––––––––––––––––––––––––-––} { } { GenerateNewCarsThread } { } { This Threads responsibility is initially add four cars to the road, then to } { continually check to see if there is a free thread in the cooperative thread } { pool, and a free thread in the pre-emptive thread pool. If so, we have what is } { needed for a car, so lets create it. Since this is a cooperative thread, we need } { to explicitally yield to other threads. } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} FUNCTION GenerateNewCarsThread(threadParam : LONGINT) : LONGINT; VAR error : OSErr; newThreadID : ThreadID; numAsyncFree : INTEGER; numToolboxFree : INTEGER; BEGIN repeat error := GetFreeThreadCount(kPreemptiveThread, numAsyncFree); { Get the count of the number of free preemptive threads in application pool } error := GetFreeThreadCount(kCooperativeThread, numToolboxFree); { Get the count of the number of free cooperative threads in application pool } IF (numAsyncFree > 0) AND (numToolboxFree > 0) THEN AddACarToTheRoad; error := YieldToAnyThread; { Yield to other threads. } until HellFreezesOver; END; {$S Simulation} {––––––––––––––––––––––––––––––––––––COOPERATIVE–––––––––––––––––––––––––––––––––––-––} { } { DisposeThreadsFromPool } { } { When I am generating a new Car generating thread I run into the problem that I } { may allocated 5 cooperative threads then fail to allocate 4 pre-emptive threads. } { This would be a real bummer in terms of memory so it would be nice if I could } { get rid of threads in the thread pool if I do not want them. } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} FUNCTION DisposeThreadsFromPool(threadStyle : ThreadStyle; howMany : INTEGER) : OSErr; VAR newThreadID : ThreadID; error : OSErr; loop : INTEGER; BEGIN DisposeThreadsFromPool := noErr; FOR loop := 1 TO howMany DO BEGIN error := NewThread(threadStyle, @DisposeThreadsFromPool, 0, 0, kUsePremadeThread + kNewSuspend, NIL, newThreadID); { Create a new thread from the application thread } { pool, its initial state is suspended. } IF error <> noErr THEN LEAVE; error := DisposeThread(newThreadID, 0, FALSE); { Dispose of a thread, do not recycle it into the application thread pool. } IF error <> noErr THEN LEAVE; END; DisposeThreadsFromPool := error; END; {$S Simulation} {––––––––––––––––––––––––––––––––––––COOPERATIVE–––––––––––––––––––––––––––––––––––-––} { } { CreateFreeThreadsForCars } { } { We need to create more threads so that more cars can be added to the road. } { Create four cooperative and four pre-emptive threads for use in the traffic } { simulation. } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} PROCEDURE CreateFreeThreadsForCars; VAR error : OSErr; newThreadID : ThreadID; BEGIN error := CreateThreadPool(kCooperativeThread, 4, 0); { Allocate four cooperative threads into the applications thread pool. } IF error = noErr THEN BEGIN error := CreateThreadPool(kPreemptiveThread, 4, 0); { Allocate four preemptive threads into the applications thread pool. } IF error <> noErr THEN BEGIN error := DisposeThreadsFromPool(kCooperativeThread, 5); Sysbeep(1); END; END ELSE Sysbeep(1); END; {$S Simulation} {––––––––––––––––––––––––––––––––––––COOPERATIVE–––––––––––––––––––––––––––––––––––-––} { } { CreateCarGeneratingThread } { } { We need to create the thread that actually gets all the cars moving on the road. } { Simply create and set running a cooperative thread. } { } { July 15, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} PROCEDURE CreateCarGeneratingThread; VAR error : OSErr; newThreadID : ThreadID; BEGIN error := CreateThreadPool(kCooperativeThread, 1, 0); { Allocate a cooperative threads into the applications thread pool. } IF error <> noErr THEN DebugStr('Failure to create car utiliation thread') ELSE error := NewThread(kCooperativeThread, @GenerateNewCarsThread, 0, 0, kUsePremadeThread, NIL, newThreadID); { Create a new thread from the exisiting thread pool. } END; {$S Simulation} {––––––––––––––––––––––––––––––––––––COOPERATIVE–––––––––––––––––––––––––––––––––––-––} { } { MarkACarForDestruction } { } { The user wishes for a car to be removed from the road. The easiest time to do } { this is when the car leaves the road and is normally requeued for another run } { down the road. Set the carType to 5 so that the car will appear white and } { needToRedraw to TRUE so that even stopped cars will be redrawn. } { } { July 16, 1992 WHK Created today } { } {––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––-––} PROCEDURE MarkACarForDestruction; VAR loop : INTEGER; theCar : AutoHandle; BEGIN IF gAutomobiles = NIL THEN BEGIN Sysbeep(1); EXIT(MarkACarForDestruction); END; FOR loop := 1 TO gTotalCarsInArray DO BEGIN theCar := ReturnCarHandle(loop); IF theCar <> NIL THEN BEGIN IF NOT(theCar^^.markedForDeath) THEN BEGIN theCar^^.markedForDeath := TRUE; theCar^^.carType := 5; theCar^^.needToRedraw := TRUE; LEAVE; END END; END; END; END.